package io.ouka.jvm.hotspot.selfread.read;

import io.ouka.jvm.hotspot.selfread.InstanceKlass;
import io.ouka.jvm.hotspot.selfread.constant.FinalConstant;
import io.ouka.jvm.hotspot.selfread.constant.MethodConstant;
import io.ouka.jvm.hotspot.selfread.domain.*;
import io.ouka.jvm.hotspot.selfread.util.DataTranslate;

import java.util.Arrays;
/**
 * @author ouka
 * MethodInfoReader
 */
public class MethodInfoReader extends AbstractReader {

    public MethodInfoReader() {
        this.iReader = new AttributeReader();
    }

    private int methodNums;
    private int methodInfoByteIndex = 0;

    @Override
    public void preRead(byte[] content) {
        System.arraycopy(content, index, FinalConstant.U2BYTE, 0, FinalConstant.u2);
        index += FinalConstant.u2;
        this.methodNums = DataTranslate.byteToUnsignedShort(FinalConstant.U2BYTE);
        byte[] bytes = new byte[content.length - index];
        System.arraycopy(content, index, bytes, 0, content.length - index);
        this.selfContent = bytes;
    }

    @Override
    public void read() {
        this.instanceKlass.setMethodLength(methodNums);

        MethodInfo[] methodInfos = new MethodInfo[methodNums];
        //一共有多少个方法 进行循环解析
        for (int i = 0; i < methodNums; i++) {
            MethodInfo methodInfo = new MethodInfo();
            methodInfos[i]=methodInfo;
            //AccessFlag
            System.arraycopy(selfContent, methodInfoByteIndex, FinalConstant.U2BYTE, 0, FinalConstant.u2);
            methodInfoByteIndex += 2;
            index += 2;
            methodInfo.setAccessFlag(DataTranslate.byteToUnsignedShort(FinalConstant.U2BYTE));
            //NameIndex
            System.arraycopy(selfContent, methodInfoByteIndex, FinalConstant.U2BYTE, 0, FinalConstant.u2);
            methodInfoByteIndex += 2;
            index += 2;
            methodInfo.setNameIndex(DataTranslate.byteToUnsignedShort(FinalConstant.U2BYTE));
            //DescriptorIndex
            System.arraycopy(selfContent, methodInfoByteIndex, FinalConstant.U2BYTE, 0, FinalConstant.u2);
            methodInfoByteIndex += 2;
            index += 2;
            methodInfo.setDescriptorIndex(DataTranslate.byteToUnsignedShort(FinalConstant.U2BYTE));
            //AttributeCount
            System.arraycopy(selfContent, methodInfoByteIndex, FinalConstant.U2BYTE, 0, FinalConstant.u2);
            methodInfoByteIndex += 2;
            index += 2;
            methodInfo.setAttributeCount(DataTranslate.byteToUnsignedShort(FinalConstant.U2BYTE));
            // 附加 目前只支持1(code attribute，异常表还有源码啥的暂时没加)
            for (int i1 = 0; i1 < methodInfo.getAttributeCount(); i1++) {
                //attr_name_index 2byte
                CodeAttribute codeAttribute = new CodeAttribute();
                System.arraycopy(selfContent, methodInfoByteIndex, FinalConstant.U2BYTE, 0, FinalConstant.u2);
                methodInfoByteIndex += FinalConstant.u2;
                index += FinalConstant.u2;
                int nameIndex = DataTranslate.byteToUnsignedShort(FinalConstant.U2BYTE);
                codeAttribute.setAttributeNameIndex(nameIndex);
                System.out.println("属性索引:" + nameIndex + "属性名" + instanceKlass.getConstantPool().getDataMap().get(nameIndex));

                //attr_length 4byte
                System.arraycopy(selfContent, methodInfoByteIndex, FinalConstant.U4BYTE, 0, FinalConstant.u4);
                int length = DataTranslate.byteArrayToInt(FinalConstant.U4BYTE);
                methodInfoByteIndex += FinalConstant.u4;
                index += FinalConstant.u4;
                codeAttribute.setAttributeLength(length);
                System.out.println("属性长度" + length);

                //max_stack 2byte
                System.arraycopy(selfContent, methodInfoByteIndex, FinalConstant.U2BYTE, 0, FinalConstant.u2);
                int maxStack = DataTranslate.byteToUnsignedShort(FinalConstant.U2BYTE);
                methodInfoByteIndex += FinalConstant.u2;
                index += FinalConstant.u2;
                codeAttribute.setMasStack(maxStack);
                System.out.println("MaxStack==>" + maxStack);

                //max_locals 2byte
                System.arraycopy(selfContent, methodInfoByteIndex, FinalConstant.U2BYTE, 0, FinalConstant.u2);
                int maxLocals = DataTranslate.byteToUnsignedShort(FinalConstant.U2BYTE);
                methodInfoByteIndex += FinalConstant.u2;
                index += FinalConstant.u2;
                codeAttribute.setMaxLocals(maxLocals);
                System.out.println("maxLocals==>" + maxLocals);

                //code_length 4byte
                System.arraycopy(selfContent, methodInfoByteIndex, FinalConstant.U4BYTE, 0, FinalConstant.u4);
                int codeLength = DataTranslate.byteArrayToInt(FinalConstant.U4BYTE);
                methodInfoByteIndex += FinalConstant.u4;
                index += FinalConstant.u4;
                codeAttribute.setCodeLength(codeLength);
                System.out.println("codeLength==>" + codeLength);


                //code  code_length byte
                byte[] bytes = new byte[codeAttribute.getCodeLength()];
                System.arraycopy(selfContent, methodInfoByteIndex, bytes, 0, codeAttribute.getCodeLength());
                methodInfoByteIndex += codeAttribute.getCodeLength();
                index += codeAttribute.getCodeLength();
                Code code = new Code(codeAttribute.getCodeLength());
                codeAttribute.setCodes(code);
                code.setCodes(bytes);
                System.out.println("code==>" + code);

                //ex_table_length 2byte
                System.arraycopy(selfContent, methodInfoByteIndex, FinalConstant.U2BYTE, 0, FinalConstant.u2);
                int exTableLength = DataTranslate.byteToUnsignedShort(FinalConstant.U2BYTE);
                methodInfoByteIndex += FinalConstant.u2;
                index += FinalConstant.u2;
                codeAttribute.setExceptionTableLength(exTableLength);
                System.out.println("exTableLength==>" + exTableLength);

                //ex_table     byte ex_table_length
                byte[] exBytes = new byte[codeAttribute.getExceptionTableLength()];
                System.arraycopy(selfContent, methodInfoByteIndex, exBytes, 0, codeAttribute.getExceptionTableLength());
                methodInfoByteIndex += codeAttribute.getExceptionTableLength();
                index += codeAttribute.getExceptionTableLength();
                codeAttribute.setExceptionTable(exBytes);
                System.out.println("ex_table ==>" + Arrays.toString(exBytes));


                //attribute_count 2byte
                System.arraycopy(selfContent, methodInfoByteIndex, FinalConstant.U2BYTE, 0, FinalConstant.u2);
                int attributeCount = DataTranslate.byteToUnsignedShort(FinalConstant.U2BYTE);
                methodInfoByteIndex += FinalConstant.u2;
                index += FinalConstant.u2;
                codeAttribute.setAttributesCount(attributeCount);
                System.out.println("attributeCount==>" + attributeCount);

                //attribute  byte[n]
                parseCodeAttribute(attributeCount, codeAttribute);

                methodInfo.getCodeAttributes()[i] = codeAttribute;
            }
        }
        this.instanceKlass.setMethods(methodInfos);
    }

    private void parseCodeAttribute(int attributeCount, CodeAttribute codeAttribute) {
        for (int i = 0; i < attributeCount; i++) {
            //属性值在常量池的索引
            System.arraycopy(selfContent,methodInfoByteIndex,FinalConstant.U2BYTE,0,FinalConstant.u2);
            methodInfoByteIndex+=FinalConstant.u2;
            index+=FinalConstant.u2;
            int attributeIndex = DataTranslate.byteToUnsignedShort(FinalConstant.U2BYTE);
            String name = (String) instanceKlass.getConstantPool().getDataMap().get(attributeIndex);
            System.out.println("解析方法的attributeIndex:"+attributeIndex+"==属性:"+name);

            //长度
            System.arraycopy(selfContent,methodInfoByteIndex,FinalConstant.U4BYTE,0,FinalConstant.u4);
            int attributeLength = DataTranslate.byteArrayToInt(FinalConstant.U4BYTE);
            methodInfoByteIndex+=FinalConstant.u4;
            index+=FinalConstant.u4;
            System.out.println("解析方法的attribute长度:"+attributeLength);

            //主体
            byte[] attributeBytes = new byte[attributeLength];
            System.arraycopy(selfContent,methodInfoByteIndex,attributeBytes,0,attributeLength);
            methodInfoByteIndex+=attributeLength;
            index+=attributeLength;

            if (name.equalsIgnoreCase(MethodConstant.LINE_NUMBER_TABLE))
                parseLineNumberTable(codeAttribute,attributeBytes);

            if (name.equalsIgnoreCase(MethodConstant.LOCAL_VARIABLE_TABLE))
                parseLocalVariableTable(codeAttribute,attributeBytes);

        }

    }

    private void parseLineNumberTable(CodeAttribute codeAttribute,byte[] bytes){

        int lineNumberTableReadIndex = 0;
        LineNumberTable lineNumberTable = new LineNumberTable();
        lineNumberTable.setInfo(bytes);
        lineNumberTable.setTableLength(bytes.length);
        codeAttribute.getAttributes().put(MethodConstant.LINE_NUMBER_TABLE,lineNumberTable);

        System.arraycopy(bytes,lineNumberTableReadIndex,FinalConstant.U2BYTE,0,FinalConstant.u2);
        lineNumberTableReadIndex+=FinalConstant.u2;
        int tableLength = DataTranslate.byteToUnsignedShort(FinalConstant.U2BYTE);
        lineNumberTable.setTableLength(tableLength);
        System.out.println("parseLineNumberTable：TableLength："+tableLength);

        if (0 != lineNumberTable.getTableLength()) {
            for (int i = 0; i < lineNumberTable.getTableLength(); i++) {
                LineNumberTable.Item item = lineNumberTable.new Item();

                System.arraycopy(bytes,lineNumberTableReadIndex,FinalConstant.U2BYTE,0,FinalConstant.u2);
                lineNumberTableReadIndex+=FinalConstant.u2;
                int startPc = DataTranslate.byteToUnsignedShort(FinalConstant.U2BYTE);
                item.setStartPc(startPc);
                System.out.println("第"+i+"个属性：：：StartPc："+startPc);

                System.arraycopy(bytes,lineNumberTableReadIndex,FinalConstant.U2BYTE,0,FinalConstant.u2);
                lineNumberTableReadIndex+=FinalConstant.u2;
                int lineNumber = DataTranslate.byteToUnsignedShort(FinalConstant.U2BYTE);
                item.setLineNumber(lineNumber);

                System.out.println("第"+i+"个属性：：：LineNumber："+lineNumber);
            }
        }

    }
    private void parseLocalVariableTable(CodeAttribute codeAttribute,byte[] bytes){
        int localVariableTableReadIndex = 0;

        LocalVariableTable localVariableTable = new LocalVariableTable();
        localVariableTable.setInfo(bytes);
        localVariableTable.setTableLength(bytes.length);
        codeAttribute.getAttributes().put(MethodConstant.LOCAL_VARIABLE_TABLE,localVariableTable);

        System.arraycopy(bytes,localVariableTableReadIndex,FinalConstant.U2BYTE,0,FinalConstant.u2);
        localVariableTableReadIndex+=FinalConstant.u2;
        int tableLength = DataTranslate.byteToUnsignedShort(FinalConstant.U2BYTE);
        localVariableTable.setTableLength(tableLength);
        System.out.println("parseLocalVariableTable：TableLength："+tableLength);

        if (0 != localVariableTable.getTableLength()) {
            for (int i = 0; i < localVariableTable.getTableLength(); i++) {
                LocalVariableTable.Item item = localVariableTable.new Item();

                System.arraycopy(bytes,localVariableTableReadIndex,FinalConstant.U2BYTE,0,FinalConstant.u2);
                localVariableTableReadIndex+=FinalConstant.u2;
                int startPc = DataTranslate.byteToUnsignedShort(FinalConstant.U2BYTE);
                item.setStartPc(startPc);
                System.out.println("第"+i+"个属性：：：StartPc："+startPc);
                System.arraycopy(bytes,localVariableTableReadIndex,FinalConstant.U2BYTE,0,FinalConstant.u2);
                localVariableTableReadIndex+=FinalConstant.u2;
                int length = DataTranslate.byteToUnsignedShort(FinalConstant.U2BYTE);
                item.setLength(length);
                System.out.println("第"+i+"个属性：：：length："+length);
                System.arraycopy(bytes,localVariableTableReadIndex,FinalConstant.U2BYTE,0,FinalConstant.u2);
                localVariableTableReadIndex+=FinalConstant.u2;
                int nameIndex = DataTranslate.byteToUnsignedShort(FinalConstant.U2BYTE);
                item.setNameIndex(nameIndex);
                System.out.println("第"+i+"个属性：：：nameIndex："+nameIndex);
                System.arraycopy(bytes,localVariableTableReadIndex,FinalConstant.U2BYTE,0,FinalConstant.u2);
                localVariableTableReadIndex+=FinalConstant.u2;
                int descriptorIndex = DataTranslate.byteToUnsignedShort(FinalConstant.U2BYTE);
                item.setDescriptorIndex(descriptorIndex);
                System.out.println("第"+i+"个属性：：：descriptorIndex："+descriptorIndex);
                System.arraycopy(bytes,localVariableTableReadIndex,FinalConstant.U2BYTE,0,FinalConstant.u2);
                localVariableTableReadIndex+=FinalConstant.u2;
                int index = DataTranslate.byteToUnsignedShort(FinalConstant.U2BYTE);
                item.setIndex(index);
                System.out.println("第"+i+"个属性：：：index："+index);
            }
        }
    }
}
